/**
 * Layui
 * MIT Licensed
 */

// 便于打包时的字符压缩
var document = window.document;
var location = window.location;

// 基础配置
var config = {
  timeout: 10, // 符合规范的模块请求最长等待秒数
  debug: false, // 是否开启调试模式
  version: false, // 是否在模块请求时加入版本号参数（以更新模块缓存）
};

// 模块加载缓存信息
var cache = {
  modules: {}, // 模块物理路径
  status: {}, // 模块加载就绪状态
  event: {}, // 模块自定义事件
  callback: {}, // 模块的回调
};

// constructor
var Class = function () {
  this.v = '__VERSION__'; // 版本号
};

// 识别预先可能定义的指定全局对象
var GLOBAL = window.LAYUI_GLOBAL || {};

// 获取 layui 所在目录
var getPath = (function () {
  var jsPath =
    document.currentScript &&
    document.currentScript.tagName.toUpperCase() === 'SCRIPT'
      ? document.currentScript.src
      : (function () {
          var js = document.getElementsByTagName('script');
          var last = js.length - 1;
          var src;
          for (var i = last; i > 0; i--) {
            if (js[i].readyState === 'interactive') {
              src = js[i].src;
              break;
            }
          }
          return src || js[last].src;
        })();

  return (config.dir =
    GLOBAL.dir || jsPath.substring(0, jsPath.lastIndexOf('/') + 1));
})();

// 异常提示
var error = function (msg, type) {
  type = type || 'warn';
  msg = '[Layui warn]: ' + msg;

  // 仅允许 error 或 warn 两种类型的提示
  if (/warn|error/.test(type.trim())) {
    type = 'warn';
  }

  window.console[type](msg);
};
var warned = Object.create(null);

var errorOnce = function (msg, type) {
  if (warned._size && warned._size > 100) {
    warned = Object.create(null);
    warned._size = 0;
  }
  if (!warned[msg]) {
    warned[msg] = true;
    warned._size = (warned._size || 0) + 1;
    error(msg, type);
  }
};

// 内置模块
var builtinModules = (config.builtin = {
  lay: 'lay', // 基础 DOM 操作
  layer: 'layer', // 弹层
  laydate: 'laydate', // 日期
  laypage: 'laypage', // 分页
  laytpl: 'laytpl', // 模板引擎
  form: 'form', // 表单集
  upload: 'upload', // 上传
  dropdown: 'dropdown', // 下拉菜单
  transfer: 'transfer', // 穿梭框
  tree: 'tree', // 树结构
  table: 'table', // 表格
  treeTable: 'treeTable', // 树表
  tabs: 'tabs', // 标签页
  tab: 'tab', // 选项卡（兼容保留）
  nav: 'nav', // 导航菜单
  breadcrumb: 'breadcrumb', // 面包屑
  progress: 'progress', // 进度条
  collapse: 'collapse', // 折叠面板
  element: 'element', // 常用元素操作
  rate: 'rate', // 评分组件
  colorpicker: 'colorpicker', // 颜色选择器
  slider: 'slider', // 滑块
  carousel: 'carousel', // 轮播
  flow: 'flow', // 流加载
  util: 'util', // 工具块
  code: 'code', // 代码修饰器
  jquery: 'jquery', // DOM 库（第三方）
  component: 'component', // 组件构建器
  i18n: 'i18n', // 国际化

  all: 'all',
  'layui.all': 'layui.all', // 聚合标识（功能性的，非真实模块）
});

/**
 * 节点加载事件
 * @param {HTMLElement} node - script 或 link 节点
 * @param {Function} done
 * @param {Function} error
 */
var onNodeLoad = function (node, done, error) {
  // 资源加载完毕
  var onCompleted = function (e) {
    var readyRegExp = /^(complete|loaded)$/;
    if (
      e.type === 'load' ||
      readyRegExp.test((e.currentTarget || e.srcElement).readyState)
    ) {
      removeListener();
      typeof done === 'function' && done(e);
    }
  };
  // 资源加载失败
  var onError = function (e) {
    removeListener();
    typeof error === 'function' && error(e);
  };

  // 移除事件
  var removeListener = function () {
    if (node.detachEvent) {
      node.detachEvent('onreadystatechange', onCompleted);
    } else {
      node.removeEventListener('load', onCompleted, false);
      node.removeEventListener('error', onError, false);
    }
  };

  // 添加事件
  if (
    node.attachEvent &&
    !(
      node.attachEvent.toString &&
      node.attachEvent.toString().indexOf('[native code') < 0
    )
  ) {
    // 此处考虑到 IE9+ load 的稳定性，固仍然采用 onreadystatechange
    node.attachEvent('onreadystatechange', onCompleted);
  } else {
    node.addEventListener('load', onCompleted, false);
    node.addEventListener('error', onError, false);
  }
};

// 获取配置及临时缓存信息
Class.prototype.cache = Object.assign(config, cache);

/**
 * 全局配置
 * @param {Object} options - 配置对象
 */
Class.prototype.config = function (options) {
  Object.assign(config, options);
  return this;
};

/**
 * 定义模块
 * @param {(string|string[])} deps - 依赖的模块列表
 * @param {Function} callback - 模块的回调
 */
Class.prototype.define = function (deps, callback) {
  var that = this;
  var useCallback = function () {
    var setModule = function (mod, exports) {
      layui[mod] = exports; // 将模块接口赋值在 layui 对象中
      cache.status[mod] = true; // 标记模块注册完成
    };
    // 执行模块的回调
    typeof callback === 'function' &&
      callback(function (mod, exports) {
        setModule(mod, exports);
        // 记录模块回调，以便需要时再执行
        cache.callback[mod] = function () {
          callback(setModule);
        };
      });
    return this;
  };

  // 若未依赖模块
  if (typeof deps === 'function') {
    callback = deps;
    deps = [];
  }

  that.use(deps, useCallback, null, 'define');
  return that;
};

/**
 * 使用模块
 * @param {(string|string[])} mods - 模块列表
 * @param {Function} callback - 回调
 */
Class.prototype.use = function (mods, callback, exports, from) {
  var that = this;
  var dir = (config.dir = config.dir ? config.dir : getPath);

  // 整理模块队列
  mods = (function () {
    if (typeof mods === 'string') {
      return [mods];
    }
    // 若第一个参数为 function ，则自动加载所有内置模块，且执行的回调即为该 function 参数；
    else if (typeof mods === 'function') {
      callback = mods;
      return ['all'];
    }
    return mods;
  })();

  // 获取 layui 静态资源所在 host
  if (!config.host) {
    config.host = (dir.match(/\/\/([\s\S]+?)\//) || [
      '//' + location.host + '/',
    ])[0];
  }

  // 若参数异常
  if (!mods) return that;

  // 若页面已经存在 jQuery 且所定义的模块依赖 jQuery，则不加载内部 jquery 模块
  if (window.jQuery && window.jQuery.fn.on) {
    that.each(mods, function (index, item) {
      if (item === 'jquery') {
        mods.splice(index, 1);
      }
    });
    layui.jquery = layui.$ = window.jQuery;
  }

  // 将模块的接口作为回调的参数传递
  exports = exports || [];

  // 加载当前队列的第一个模块
  var item = mods[0];
  var modInfo = that.modules[item]; // 当前模块信息
  // 是否为外部模块，即无需遵循 layui 轻量级模块规范的任意第三方组件。
  var isExternalModule = typeof modInfo === 'object';

  // 回调触发
  var onCallback = function () {
    exports.push(layui[item]);
    mods.length > 1
      ? that.use(mods.slice(1), callback, exports, from)
      : typeof callback === 'function' &&
        (function () {
          // 保证文档加载完毕再执行回调
          if (
            layui.jquery &&
            typeof layui.jquery === 'function' &&
            from !== 'define'
          ) {
            return layui.jquery(function () {
              callback.apply(layui, exports);
            });
          }
          callback.apply(layui, exports);
        })();
  };

  // 回调轮询
  var pollCallback = function () {
    var timeout = 0; // 超时计数器（秒）
    var delay = 5; // 轮询等待毫秒数

    // 轮询模块加载完毕状态
    (function poll() {
      if (++timeout > (config.timeout * 1000) / delay) {
        return error(item + ' is not a valid module', 'error');
      }

      // 根据模块加载完毕的标志来完成轮询
      // 若为任意外部模块，则标志为该模块接口是否存在；
      // 若为遵循 layui 规范的模块，则标志为模块的 status 状态值
      (
        isExternalModule
          ? (layui[item] = window[modInfo.api])
          : cache.status[item]
      )
        ? onCallback()
        : setTimeout(poll, delay);
    })();
  };

  // 若为发行版，则内置模块不必异步加载
  if (mods.length === 0 || (layui['layui.all'] && builtinModules[item])) {
    return (onCallback(), that);
  }

  // 当前模块所在路径
  var modSrc = isExternalModule ? modInfo.src : modInfo;

  // 基础路径
  var basePath = builtinModules[item]
    ? dir + 'modules/' // 若为内置模块，则按照默认 dir 参数拼接模块 URL
    : modSrc
      ? ''
      : config.base; // 若为扩展模块，且模块路径已设置，则不必再重复拼接基础路径

  // 若从 layui.modules 为获取到模块路径, 则将传入的模块名视为路径名
  if (!modSrc) modSrc = item;

  // 过滤空格符和 .js 后缀
  modSrc = modSrc.replace(/\s/g, '').replace(/\.js[^/.]*$/, '');

  // 拼接最终模块 URL
  var url = basePath + modSrc + '.js';

  // 若扩展模块对象已经存在，则不必再重复加载
  if (!cache.modules[item] && layui[item]) {
    cache.modules[item] = url; // 并记录起该扩展模块的 url
  }

  // 首次加载模块
  if (!cache.modules[item]) {
    var head = document.getElementsByTagName('head')[0];
    var node = document.createElement('script');

    node.async = true;
    node.charset = 'utf-8'; // 避免 IE9 的编码问题
    node.src =
      url +
      (function () {
        var version =
          config.version === true
            ? config.v || new Date().getTime()
            : config.version || '';
        return version ? '?v=' + version : '';
      })();

    head.appendChild(node);

    // 节点加载事件
    onNodeLoad(
      node,
      function () {
        head.removeChild(node);
        pollCallback();
      },
      function () {
        head.removeChild(node);
      },
    );

    // 模块已首次加载的标记
    cache.modules[item] = url;
  } else {
    // 再次 use 模块
    pollCallback();
  }

  return that;
};

// 记录全部模块
Class.prototype.modules = Object.assign({}, builtinModules);

/**
 * 拓展模块
 * @param {Object} settings - 拓展模块的配置
 */
Class.prototype.extend = function (settings) {
  var that = this;
  var base = config.base || '';
  var firstSymbolEXP = /^\{\/\}/; // 模块单独路径首字符表达式

  settings = settings || {};

  // 遍历拓展模块
  for (var modName in settings) {
    if (that[modName] || that.modules[modName]) {
      // 验证模块是否被占用
      error('the ' + modName + ' module already exists, extend failure');
    } else {
      var modInfo = settings[modName];
      // 若直接传入模块路径字符
      if (typeof modInfo === 'string') {
        // 判断传入的模块路径是否特定首字符
        // 若存在特定首字符，则模块 URL 即为该首字符后面紧跟的字符
        // 否则，则按照 config.base 路径进行拼接
        if (firstSymbolEXP.test(modInfo)) base = '';
        modInfo = (base + modInfo).replace(firstSymbolEXP, '');
      }
      that.modules[modName] = modInfo;
    }
  }

  return that;
};

/**
 * 弃用指定的模块，以便重新扩展新的同名模块。
 * @param {(string|string[])} mods - 模块列表
 */
Class.prototype.disuse = function (mods) {
  var that = this;
  mods = that.isArray(mods) ? mods : [mods];
  that.each(mods, function (index, item) {
    delete that[item];
    delete builtinModules[item];
    delete that.modules[item];
    delete cache.status[item];
    delete cache.modules[item];
  });
  return that;
};

/**
 * 获取节点的 style 属性值
 * currentStyle.getAttribute 参数为 camelCase 形式的字符串
 * @param {HTMLElement} node - 节点
 * @param {string} name - 属性名
 * @returns 属性值
 */
Class.prototype.getStyle = function (node, name) {
  var style = node.currentStyle
    ? node.currentStyle
    : window.getComputedStyle(node, null);
  return style.getPropertyValue
    ? style.getPropertyValue(name)
    : style.getAttribute(
        name.replace(/-(\w)/g, function (_, c) {
          return c ? c.toUpperCase() : '';
        }),
      );
};

/**
 * CSS 外部加载器
 * @param {string} href - 外部 CSS 文件路径
 * @param {Function} callback - 回调函数
 * @param {string} id - 定义 link 标签的 id
 */
Class.prototype.link = function (href, callback, id) {
  var that = this;
  var head = document.getElementsByTagName('head')[0];
  var hasCallback = typeof callback === 'function';

  // 若第二个参数为 string 类型，则该参数为 id
  if (typeof callback === 'string') {
    id = callback;
  }

  // 若加载多个
  if (typeof href === 'object') {
    var isArr = that.type(id) === 'array';
    return that.each(href, function (index, value) {
      that.link(
        value,
        index === href.length - 1 && callback,
        isArr && id[index],
      );
    });
  }

  // 若未传入 id ，则取路径 `//` 后面的字符拼接为 id，不含.与参数
  id = id || href.replace(/^(#|(http(s?)):\/\/|\/\/)|\.|\/|\?.+/g, '');
  id = 'layuicss-' + id;

  var link = document.getElementById(id);

  // 初始创建节点
  if (!link) {
    link = document.createElement('link');
    link.href = href + (config.debug ? '?v=' + new Date().getTime() : '');
    link.rel = 'stylesheet';
    link.id = id;
    head.appendChild(link);
  }

  // 若加载已完成，则直接执行回调函数
  if (link.__lay_readyState__ === 'complete') {
    hasCallback && callback(link);
    return that;
  }

  // 初始加载
  onNodeLoad(
    link,
    function () {
      link.__lay_readyState__ = 'complete';
      hasCallback && callback(link);
    },
    function () {
      error(href + ' load error', 'error');
      head.removeChild(link); // 移除节点
    },
  );

  return that;
};

/**
 * CSS 内部加载器
 * @param {string} modName - 模块名
 */
Class.prototype.addcss = function (modName, callback, id) {
  return layui.link(config.dir + 'css/' + modName, callback, id);
};

/**
 * 获取执行定义模块时的回调函数，factory 为向下兼容
 * @param {string} modName - 模块名
 * @returns {Function}
 */
Class.prototype.factory = function (modName) {
  if (layui[modName]) {
    return typeof config.callback[modName] === 'function'
      ? config.callback[modName]
      : null;
  }
};

/**
 * 图片预加载
 * @param {string} url - 图片路径
 * @param {Function} callback - 成功回调
 * @param {Function} error - 错误回调
 */
Class.prototype.img = function (url, callback, error) {
  var img = new Image();
  img.src = url;
  if (img.complete) {
    return callback(img);
  }
  img.onload = function () {
    img.onload = null;
    typeof callback === 'function' && callback(img);
  };
  img.onerror = function (e) {
    img.onerror = null;
    typeof error === 'function' && error(e);
  };
};

/**
 * location.hash 路由解析
 * @param {string} hash 值
 * @returns {Object}
 */
Class.prototype.router = Class.prototype.hash = function (hash) {
  hash = hash || location.hash;
  var that = this;
  var data = {
    path: [],
    pathname: [],
    search: {},
    hash: (hash.match(/[^#](#.*$)/) || [])[1] || '',
    href: '',
  };

  // 禁止非 hash 路由规范
  if (!/^#/.test(hash)) {
    return data;
  }

  hash = hash.replace(/^#/, '');
  data.href = hash;
  hash = hash.replace(/([^#])(#.*$)/, '$1').split('/') || [];

  // 提取 Hash 结构
  that.each(hash, function (index, item) {
    /^\w+=/.test(item)
      ? (function () {
          item = item.split('=');
          data.search[item[0]] = item[1];
        })()
      : data.path.push(item);
  });

  data.pathname = data.path; // path → pathname, 与 layui.url 一致
  return data;
};

/**
 * URL 解析
 * @param {string} href - url 路径
 * @returns {Object}
 */
Class.prototype.url = function (href) {
  var that = this;
  var data = {
    // 提取 url 路径
    pathname: (function () {
      var pathname = href
        ? (function () {
            var str = (href.match(/\.[^.]+?\/.+/) || [])[0] || '';
            return str.replace(/^[^/]+/, '').replace(/\?.+/, '');
          })()
        : location.pathname;
      return pathname.replace(/^\//, '').split('/');
    })(),

    // 提取 url 参数
    search: (function () {
      var obj = {};
      var search = (
        href
          ? (function () {
              var str = (href.match(/\?.+/) || [])[0] || '';
              return str.replace(/#.+/, '');
            })()
          : location.search
      )
        .replace(/^\?+/, '')
        .split('&'); // 去除 ?，按 & 分割参数

      // 遍历分割后的参数
      that.each(search, function (index, item) {
        var _index = item.indexOf('=');
        var key = (function () {
          // 提取 key
          if (_index < 0) {
            return item.substr(0, item.length);
          } else if (_index === 0) {
            return false;
          } else {
            return item.substr(0, _index);
          }
        })();
        // 提取 value
        if (key) {
          obj[key] = _index > 0 ? item.substr(_index + 1) : null;
        }
      });

      return obj;
    })(),

    // 提取 Hash
    hash: that.router(
      (function () {
        return href ? (href.match(/#.+/) || [])[0] || '/' : location.hash;
      })(),
    ),
  };

  return data;
};

/**
 * 本地持久存储
 * @param {string} table - 表名
 * @param {Object} settings - 设置项
 * @param {Storage} storage - 存储对象，localStorage 或 sessionStorage
 * @returns {Object}
 */
Class.prototype.data = function (table, settings, storage) {
  table = table || 'layui';
  storage = storage || localStorage;

  // 如果 settings 为 null，则删除表
  if (settings === null) {
    return delete storage[table];
  }

  settings = typeof settings === 'object' ? settings : { key: settings };

  var data;

  try {
    data = JSON.parse(storage[table]);
  } catch {
    data = {};
  }

  if ('value' in settings) data[settings.key] = settings.value;
  if (settings.remove) delete data[settings.key];
  storage[table] = JSON.stringify(data);

  return settings.key ? data[settings.key] : data;
};

/**
 * 本地临时存储
 * @param {string} table - 表名
 * @param {Object} settings - 设置项
 * @returns {Object}
 */
Class.prototype.sessionData = function (table, settings) {
  return this.data(table, settings, sessionStorage);
};

/**
 * 设备信息
 * @param {string} key - 任意 key
 * @returns {Object}
 */
Class.prototype.device = function (key) {
  var agent = navigator.userAgent.toLowerCase();

  // 获取版本号
  var getVersion = function (label) {
    var exp = new RegExp(label + '/([^\\s\\_\\-]+)');
    label = (agent.match(exp) || [])[1];
    return label || false;
  };

  // 返回结果集
  var result = {
    os: (function () {
      // 底层操作系统
      if (/windows/.test(agent)) {
        return 'windows';
      } else if (/linux/.test(agent)) {
        return 'linux';
      } else if (/iphone|ipod|ipad|ios/.test(agent)) {
        return 'ios';
      } else if (/mac/.test(agent)) {
        return 'mac';
      }
    })(),
    ie: (function () {
      // ie 版本
      return !!window.ActiveXObject || 'ActiveXObject' in window
        ? (agent.match(/msie\s(\d+)/) || [])[1] || '11' // 由于 ie11 并没有 msie 的标识
        : false;
    })(),
    weixin: getVersion('micromessenger'), // 是否微信
  };

  // 任意的 key
  if (key && !result[key]) {
    result[key] = getVersion(key);
  }

  // 移动设备
  result.android = /android/.test(agent);
  result.ios = result.os === 'ios';
  result.mobile = result.android || result.ios;

  return result;
};

// 提示
Class.prototype.hint = function () {
  return {
    error: error,
    errorOnce: errorOnce,
  };
};

/**
 * typeof 类型细分 -> string/number/boolean/undefined/null、object/array/function/…
 * @param {*} operand - 任意值
 * @returns {string}
 */
Class.prototype._typeof = Class.prototype.type = function (operand) {
  if (operand === null) return String(operand);

  // 细分引用类型
  return typeof operand === 'object' || typeof operand === 'function'
    ? (function () {
        var type =
          Object.prototype.toString.call(operand).match(/\s(.+)\]$/) || []; // 匹配类型字符
        var classType = 'Function|Array|Date|RegExp|Object|Error|Symbol'; // 常见类型字符

        type = type[1] || 'Object';

        // 除匹配到的类型外，其他对象均返回 object
        return new RegExp('\\b(' + classType + ')\\b').test(type)
          ? type.toLowerCase()
          : 'object';
      })()
    : typeof operand;
};

/**
 * 对象是否具备数组结构（此处为兼容 jQuery 对象）
 * @param {Object} obj - 任意对象
 * @returns {boolean}
 */
Class.prototype._isArray = Class.prototype.isArray = function (obj) {
  var that = this;
  var len;
  var type = that.type(obj);

  if (!obj || typeof obj !== 'object' || obj === window) return false;

  len = 'length' in obj && obj.length; // 兼容 ie
  return (
    type === 'array' ||
    len === 0 ||
    (typeof len === 'number' && len > 0 && len - 1 in obj) // 兼容 jQuery 对象
  );
};

/**
 * 遍历
 * @param {Object} obj - 任意对象
 * @param {Function} fn - 遍历回调
 */
Class.prototype.each = function (obj, fn) {
  var key;
  var that = this;
  var callback = function (key, obj) {
    return fn.call(obj[key], key, obj[key]);
  };

  if (typeof fn !== 'function') {
    return that;
  }

  obj = obj || [];

  // 优先处理数组结构
  if (that.isArray(obj)) {
    for (key = 0; key < obj.length; key++) {
      if (callback(key, obj)) break;
    }
  } else {
    for (key in obj) {
      if (callback(key, obj)) break;
    }
  }

  return that;
};

/**
 * 将数组中的成员对象按照某个 key 的 value 值进行排序
 * @param {Object[]} arr - 任意数组
 * @param {string} key - 任意 key
 * @param {boolean} desc - 是否降序
 * @param {boolean} notClone - 是否不对 arr 进行克隆
 * @returns {Object[]}
 */
Class.prototype.sort = function (arr, key, desc, notClone) {
  var that = this;
  var clone = notClone ? arr || [] : JSON.parse(JSON.stringify(arr || []));

  // 若未传入 key，则直接返回原对象
  if (that.type(arr) === 'object' && !key) {
    return clone;
  } else if (typeof arr !== 'object') {
    // 若 arr 非对象
    return [clone];
  }

  // 开始排序
  clone.sort(function (o1, o2) {
    var v1 = o1[key];
    var v2 = o2[key];

    /*
     * 特殊数据
     * 若比较的成员均非对象
     */

    // 若比较的成员均为数字
    if (!isNaN(o1) && !isNaN(o2)) return o1 - o2;

    // 若比较的成员只存在某一个非对象
    if (!isNaN(o1) && isNaN(o2)) {
      if (key && typeof o2 === 'object') {
        v1 = o1;
      } else {
        return -1;
      }
    } else if (isNaN(o1) && !isNaN(o2)) {
      if (key && typeof o1 === 'object') {
        v2 = o2;
      } else {
        return 1;
      }
    }

    /*
     * 正常数据
     * 即成员均为对象，也传入了对比依据： key
     * 若 value 为数字，按「大小」排序；若 value 非数字，则按「字典序」排序
     */

    // value 是否为数字
    var isNum = [!isNaN(v1), !isNaN(v2)];

    // 若为数字比较
    if (isNum[0] && isNum[1]) {
      if (v1 && !v2 && v2 !== 0) {
        // 数字 vs 空
        return 1;
      } else if (!v1 && v1 !== 0 && v2) {
        // 空 vs 数字
        return -1;
      } else {
        // 数字 vs 数字
        return v1 - v2;
      }
    }

    /**
     * 字典序排序
     */

    // 若为非数字比较
    if (!isNum[0] && !isNum[1]) {
      // 字典序比较
      if (v1 > v2) {
        return 1;
      } else if (v1 < v2) {
        return -1;
      } else {
        return 0;
      }
    }

    // 若为混合比较
    if (isNum[0] || !isNum[1]) {
      // 数字 vs 非数字
      return -1;
    } else if (!isNum[0] || isNum[1]) {
      // 非数字 vs 数字
      return 1;
    }
  });

  desc && clone.reverse(); // 倒序
  return clone;
};

/**
 * 阻止事件冒泡
 * @param {Event} thisEvent - 事件对象
 */
Class.prototype.stope = function (thisEvent) {
  try {
    thisEvent.stopPropagation();
  } catch {
    thisEvent.cancelBubble = true;
  }
};

// 字符常理
var EV_REMOVE = 'LAYUI-EVENT-REMOVE';

/**
 * 自定义模块事件
 * @param {string} modName - 模块名
 * @param {string} events - 事件名
 * @param {Function} callback - 回调
 * @returns {Object}
 */
Class.prototype.onevent = function (modName, events, callback) {
  if (typeof modName !== 'string' || typeof callback !== 'function') {
    return this;
  }
  return Class.event(modName, events, null, callback);
};

/**
 * 执行自定义模块事件
 * @param {string} modName - 模块名
 * @param {string} events - 事件名
 * @param {Object} params - 参数
 * @param {Function} fn - 回调
 */
Class.prototype.event = Class.event = function (modName, events, params, fn) {
  var that = this;
  var result = null;
  var filter = (events || '').match(/\((.*)\)$/) || []; // 提取事件过滤器字符结构，如：select(xxx)
  var eventName = (modName + '.' + events).replace(filter[0], ''); // 获取事件名称，如：form.select
  var filterName = filter[1] || ''; // 获取过滤器名称, 如：xxx
  var callback = function (_, item) {
    var res = item && item.call(that, params);
    res === false && result === null && (result = false);
  };

  // 如果参数传入特定字符，则执行移除事件
  if (params === EV_REMOVE) {
    delete (that.cache.event[eventName] || {})[filterName];
    return that;
  }

  // 添加事件
  if (fn) {
    cache.event[eventName] = cache.event[eventName] || {};

    if (filterName) {
      // 带 filter 不支持重复事件
      cache.event[eventName][filterName] = [fn];
    } else {
      // 不带 filter 处理的是所有的同类事件，应该支持重复事件
      cache.event[eventName][filterName] =
        cache.event[eventName][filterName] || [];
      cache.event[eventName][filterName].push(fn);
    }
    return this;
  }

  // 执行事件回调
  layui.each(cache.event[eventName], function (key, item) {
    // 执行当前模块的全部事件
    if (filterName === '{*}') {
      layui.each(item, callback);
      return;
    }

    // 执行指定事件
    key === '' && layui.each(item, callback);
    filterName && key === filterName && layui.each(item, callback);
  });

  return result;
};

/**
 * 新增模块事件
 * @param {string} events - 事件名
 * @param {string} modName - 模块名
 * @param {Function} callback - 回调
 * @returns {Object}
 */
Class.prototype.on = function (events, modName, callback) {
  var that = this;
  return that.onevent.call(that, modName, events, callback);
};

/**
 * 移除模块事件
 * @param {string} events - 事件名
 * @param {string} modName - 模块名
 * @returns {Object}
 */
Class.prototype.off = function (events, modName) {
  var that = this;
  return that.event.call(that, modName, events, EV_REMOVE);
};

/**
 * 防抖
 * @param {Function} func - 回调
 * @param {number} wait - 延时执行的毫秒数
 * @returns {Function}
 */
Class.prototype.debounce = function (func, wait) {
  var timeout;
  return function () {
    var context = this;
    var args = arguments;
    clearTimeout(timeout);
    timeout = setTimeout(function () {
      func.apply(context, args);
    }, wait);
  };
};

/**
 * 节流
 * @param {Function} func - 回调
 * @param {number} wait - 不重复执行的毫秒数
 */
Class.prototype.throttle = function (func, wait) {
  var cooldown = false;
  return function () {
    var context = this;
    var args = arguments;
    if (!cooldown) {
      func.apply(context, args);
      cooldown = true;
      setTimeout(function () {
        cooldown = false;
      }, wait);
    }
  };
};

const layui = new Class();

// 阻止 layui.use 加载内部模块
layui.all = true;
layui['layui.all'] = 'layui.all';

// export
export { layui, layui as default };
